[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto
skywatched.app
6.2 kB
181 lines
1<script lang="ts">
2 import { type PageData } from './$types';
3 import { cn, nameToId } from '$lib/utils';
4 import { rateMovieModal } from '$lib/state/modals.svelte';
5 import { videoPlayer } from '$lib/state/video.svelte';
6 import { watchedItems } from '$lib/state/user.svelte';
7
8 import Container from '$lib/Components/Layout/Container.svelte';
9 import ItemsList from '$lib/Components/Items/ItemsList.svelte';
10 import ReviewList from '$lib/Components/Items/ReviewList.svelte';
11 import Avatar from '$lib/Components/User/Avatar.svelte';
12 import { page } from '$app/stores';
13
14 let { data }: { data: PageData } = $props();
15</script>
16
17<svelte:head>
18 <title>{data.item.title} | skywatched</title>
19
20 <meta name="description" content={`Rate and review "${data.item.title}" on skywatched`} />
21
22 <meta property="og:url" content={$page.url.href} />
23 <meta property="og:type" content="website" />
24 <meta property="og:title" content="{data.item.title} | skywatched.app" />
25 <meta property="og:description" content={`Rate and review "${data.item.title}" on skywatched`} />
26 <meta property="og:image" content="{$page.url.href}/og.png" />
27
28 <meta name="twitter:card" content="summary_large_image" />
29 <meta property="twitter:domain" content="skywatched.app" />
30 <meta property="twitter:url" content={$page.url.href} />
31 <meta name="twitter:title" content="{data.item.title} | skywatched.app" />
32 <meta name="twitter:description" content={`Rate and review "${data.item.title}" on skywatched`} />
33 <meta name="twitter:image" content="{$page.url.href}/og.png" />
34</svelte:head>
35
36{#snippet buttons()}
37 {#if data.user && !watchedItems.hasRated(data.item)}
38 <button
39 class={cn(
40 'inline-flex items-center gap-2 rounded-md bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-75 hover:bg-white/20 ',
41 watchedItems.hasRated(data.item)
42 ? 'bg-green-500/10 text-green-400 hover:bg-green-500/20'
43 : ''
44 )}
45 onclick={() => {
46 rateMovieModal.show({
47 ...data.item
48 });
49 }}
50 >
51 rate {data.kind === 'movie' ? 'movie' : 'show'}
52 </button>
53 {/if}
54 {#if data.trailer}
55 <button
56 onclick={() => videoPlayer.show(data.trailer ?? '')}
57 type="button"
58 class="inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600"
59 >
60 <svg
61 xmlns="http://www.w3.org/2000/svg"
62 viewBox="0 0 24 24"
63 fill="currentColor"
64 class="-ml-0.5 size-5"
65 >
66 <path
67 d="M4.5 4.5a3 3 0 0 0-3 3v9a3 3 0 0 0 3 3h8.25a3 3 0 0 0 3-3v-9a3 3 0 0 0-3-3H4.5ZM19.94 18.75l-2.69-2.69V7.94l2.69-2.69c.944-.945 2.56-.276 2.56 1.06v11.38c0 1.336-1.616 2.005-2.56 1.06Z"
68 />
69 </svg>
70
71 Trailer
72 </button>
73 {/if}
74{/snippet}
75
76<img
77 src="https://image.tmdb.org/t/p/w780{data.item.backdrop_path}"
78 alt=""
79 class="fixed h-full w-full object-cover object-center opacity-20"
80/>
81<div class="fixed inset-0 h-full w-full bg-black/50"></div>
82
83<Container class="relative z-10 pb-8 pt-4">
84 <div class="flex gap-4 px-4 pt-8">
85 <img
86 src="https://image.tmdb.org/t/p/w500{data.item.poster_path}"
87 alt=""
88 class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44"
89 style:--name={`poster-${data.item.ref}`}
90 />
91 <div class="flex flex-col gap-4">
92 <div
93 class="title max-w-xl text-3xl font-semibold text-white sm:text-4xl"
94 style:--name={`title-${data.item.ref}`}
95 >
96 {data.item.title ?? data.item.name}
97 </div>
98
99 {#if data.settings?.streaming_region?.code && data.watchProviders[data.settings.streaming_region.code]?.flatrate}
100 <div class="mt-2 text-sm text-white sm:mt-4">
101 <div class="mb-2 flex flex-wrap gap-4 text-xs font-medium">
102 stream on
103 <span class="flex items-center gap-2 text-base-400"
104 >from <a
105 href="https://www.justwatch.com"
106 target="_blank"
107 class="text-base-300 hover:text-accent-300"
108 >
109 <img src="/justwatch_logo.svg" alt="justwatch" class="h-3" />
110 </a></span
111 >
112 </div>
113 <a
114 href={data.watchProviders[data.settings.streaming_region.code].link}
115 target="_blank"
116 class="flex flex-wrap gap-2"
117 >
118 {#each data.watchProviders[data.settings.streaming_region.code].flatrate as provider}
119 <img
120 src="https://image.tmdb.org/t/p/w500{provider.logo_path}"
121 alt={provider.provider_name}
122 class="size-8 rounded-md border border-base-800 md:size-12"
123 />
124 {/each}
125 </a>
126 </div>
127 {/if}
128 <div class="hidden gap-2 sm:flex">
129 {@render buttons()}
130 </div>
131 </div>
132 </div>
133
134 <div class="px-4 pt-4 text-sm text-white">
135 <div class="mb-4 flex gap-2 sm:hidden">
136 {@render buttons()}
137 </div>
138 <div class="mb-4 max-w-2xl text-sm text-white">
139 <div class="mb-2 text-lg font-semibold">overview</div>
140 {data.item.overview}
141 </div>
142 </div>
143
144 {#if data.ratings.length > 0}
145 <div class="mt-8 px-4 text-lg font-semibold">reviews</div>
146
147 <ReviewList reviews={data.ratings} showMovieDetails={false} class="" />
148 {/if}
149
150 {#if data.recommendations.length > 0}
151 <div class="px-4 pt-4 text-sm text-white">
152 <div class="mb-2 text-lg font-semibold">recommendations</div>
153
154 <ItemsList items={data.recommendations} showMark={!!data.user} />
155 </div>
156 {/if}
157
158 {#if data.cast.length > 0}
159 <div class="px-4 pb-8 pt-4 text-sm text-white">
160 <div class="mb-2 text-lg font-semibold">cast</div>
161
162 <div class={cn('flex gap-x-6 overflow-x-auto')}>
163 {#each data.cast as castMember}
164 <a
165 href={`/cast/${castMember.id}-${nameToId(castMember.name)}`}
166 class="flex flex-col items-center gap-1"
167 >
168 <Avatar
169 src={castMember.profile_path
170 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path
171 : undefined}
172 size="size-32"
173 />
174 <div class="text-center text-xs font-medium">{castMember.name}</div>
175 <div class="text-center text-xs text-base-400">{castMember.character}</div>
176 </a>
177 {/each}
178 </div>
179 </div>
180 {/if}
181</Container>